import pandas as pd
import numpy as np
import statsmodels.api as sm
import os
import matplotlib
import matplotlib.pyplot as plt
import warnings

# 忽略特定警告
warnings.filterwarnings("ignore", category=UserWarning, message="Glyph 178")
warnings.filterwarnings("ignore", category=UserWarning,
                        message="FixedFormatter should only be used together with FixedLocator")

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
matplotlib.rcParams['font.size'] = 12  # 设置全局字体大小

# 读取当前数据
file_path = 'E:/M/MathModel/23国赛C题/ds/23年6月24到30.xlsx'
df = pd.read_excel(file_path)

# 计算利润并获取前27个单品
df['单品利润'] = (df['销售单价(元/千克)'] - df['批发价']) * df['销量(千克)']
profit_by_item = df.groupby('单品名称')['单品利润'].sum().reset_index()
top27_items = profit_by_item.sort_values('单品利润', ascending=False).head(27)['单品名称'].tolist()

# 筛选前27个单品的数据
df_top27 = df[df['单品名称'].isin(top27_items)]

# 转换销售日期为datetime类型
df_top27['销售日期'] = pd.to_datetime(df_top27['销售日期'])

# 按日期和单品分组计算每日销量和平均售价
daily_data = df_top27.groupby(['销售日期', '单品名称']).agg(
    总销量=('销量(千克)', 'sum'),
    平均售价=('销售单价(元/千克)', 'mean')
).reset_index()

# 创建输出目录
output_dir = 'E:/M/MathModel/23国赛C题/ds/销售分析图表'
os.makedirs(output_dir, exist_ok=True)

# 为每个单品进行回归分析
results = []
for item in top27_items:
    item_data = daily_data[daily_data['单品名称'] == item]

    # 检查数据点数量
    if len(item_data) < 2:
        print(f"警告: {item} 数据点不足({len(item_data)}), 跳过回归分析")
        continue

    # 准备回归数据
    X = item_data['平均售价'].values
    y = item_data['总销量'].values

    # 检查价格是否有变化
    if np.unique(X).size == 1:  # 所有价格相同
        # 计算平均销量
        avg_sales = np.mean(y)
        results.append({
            '单品名称': item,
            '斜率': 0,  # 价格变化对销量无影响
            '截距': avg_sales,
            'R²': 0,  # 无法解释销量变化
            '回归方程': f"Y = {avg_sales:.4f} (价格不变)",
            '数据点数': len(item_data)
        })
        print(f"信息: {item} 价格无变化，使用平均销量")
        continue

    # 添加常数项 (截距)
    X_const = sm.add_constant(X)

    try:
        # 执行线性回归
        model = sm.OLS(y, X_const).fit()

        # 提取回归系数
        intercept = model.params[0]
        slope = model.params[1]
        r_squared = model.rsquared

        # 保存结果
        results.append({
            '单品名称': item,
            '斜率': slope,
            '截距': intercept,
            'R²': r_squared,
            '回归方程': f"Y = {slope:.4f}X + {intercept:.4f}" if intercept >= 0
            else f"Y = {slope:.4f}X - {abs(intercept):.4f}",
            '数据点数': len(item_data)
        })

        # 绘制回归图
        plt.figure(figsize=(10, 6))
        plt.scatter(X, y, label='实际数据点', color='blue')

        # 绘制回归线
        x_min = min(X) * 0.9 if min(X) > 0 else min(X) * 1.1
        x_max = max(X) * 1.1
        x_vals = np.linspace(x_min, x_max, 100)
        y_vals = slope * x_vals + intercept
        plt.plot(x_vals, y_vals, 'r-', label=f'回归线: {results[-1]["回归方程"]}')

        # 添加数据点标签
        for i, (x_val, y_val) in enumerate(zip(X, y)):
            plt.text(x_val, y_val, f"{item_data['销售日期'].iloc[i].strftime('%m-%d')}",
                     fontsize=9, ha='left', va='bottom')

        plt.xlabel('平均售价(元/千克)')
        plt.ylabel('销量(千克)')
        # 使用普通2代替上标²
        plt.title(f'{item} 销量与售价关系 (n={len(item_data)}, R2={r_squared:.4f})')
        plt.legend()
        plt.grid(True)
        plt.savefig(f'{output_dir}/回归分析_{item}.png', dpi=150)
        plt.close()

    except Exception as e:
        print(f"错误: {item} 回归分析失败 - {str(e)}")
        # 即使回归失败，也保存基本数据
        results.append({
            '单品名称': item,
            '斜率': np.nan,
            '截距': np.nan,
            'R²': np.nan,
            '回归方程': f"无法计算: {str(e)}",
            '数据点数': len(item_data)
        })

# 创建结果DataFrame并保存
if results:
    results_df = pd.DataFrame(results)
    results_df = results_df[['单品名称', '回归方程', '斜率', '截距', 'R²', '数据点数']]

    # 尝试按R²排序，如果失败则按单品名称排序
    try:
        results_df = results_df.sort_values('R²', ascending=False)
    except:
        results_df = results_df.sort_values('单品名称')

    # 打印结果表格
    print("\n回归分析结果:")
    print(results_df.to_string(index=False))

    # 保存到Excel
    results_df.to_excel(f'{output_dir}/回归分析结果.xlsx', index=False)
    print(f"回归分析结果已保存至: {output_dir}/回归分析结果.xlsx")

    # 保存所有回归方程到文本文件
    with open(f'{output_dir}/回归方程汇总.txt', 'w', encoding='utf-8') as f:
        f.write("单品销量与定价回归方程汇总:\n")
        f.write("=" * 50 + "\n")
        for idx, row in results_df.iterrows():
            f.write(f"{row['单品名称']}:\n")
            f.write(f"  回归方程: {row['回归方程']}\n")
            f.write(f"  R²值: {row['R²'] if not pd.isna(row['R²']) else 'N/A'}\n")
            f.write(f"  数据点数: {row['数据点数']}\n")
            f.write("-" * 50 + "\n")
        print(f"回归方程汇总已保存至: {output_dir}/回归方程汇总.txt")
else:
    print("没有找到足够数据进行回归分析")

print(f"\n处理完成! 结果保存在: {output_dir}")